
#include "GameBob.h"

CGameBob::CGameBob()
{
	m_lpdd = NULL;
	memset(&m_bob, 0 ,sizeof(m_bob));
}

CGameBob::~CGameBob()
{
	Destroy();
}

int CGameBob::Init(LPDIRECTDRAW7 lpdd)
{
	m_lpdd = lpdd;

	return 0;
}

int CGameBob::Create(int x, int y,          // initial posiiton
					 int width, int height,	// size of bob
					 int minX, int minY, int maxX, int maxY,
					 int num_frames,			// number of frames
					 int attr,				// attrs
					 int mem_flags,			// memory flags in DD format
					 USHORT color_key_value, // default color key
					 int bpp)                // bits per pixel

{
	// Create the BOB object, note that all BOBs 
	// are created as offscreen surfaces in VRAM as the
	// default, if you want to use system memory then
	// set flags equal to:
	// DDSCAPS_SYSTEMMEMORY 
	// for video memory you can create either local VRAM surfaces or AGP 
	// surfaces via the second set of constants shown below in the regular expression
	// DDSCAPS_VIDEOMEMORY | (DDSCAPS_NONLOCALVIDMEM | DDSCAPS_LOCALVIDMEM ) 


	DDSURFACEDESC2 ddsd; // used to create surface
	int index;           // looping var

	// set state and attributes of BOB
	m_bob.state          = BOB_STATE_ALIVE;
	m_bob.attr           = attr;
	m_bob.anim_state     = 0;
	m_bob.counter_1      = 0;     
	m_bob.counter_2      = 0;
	m_bob.max_count_1    = 0;
	m_bob.max_count_2    = 0;

	m_bob.curr_frame     = 0;
	m_bob.num_frames     = num_frames;
	m_bob.bpp            = bpp;
	m_bob.curr_animation = 0;
	m_bob.anim_counter   = 0;
	m_bob.anim_index     = 0;
	m_bob.anim_count_max = 0; 
	m_bob.x              = x;
	m_bob.y              = y;
	m_bob.xv             = 0;
	m_bob.yv             = 0;
	m_bob.minX			 = minX;
	m_bob.minY			 = minY;
	m_bob.maxX           = maxX;
	m_bob.maxY			 = maxY;

	m_bob.width_fill	 = 0;

	// set dimensions of the new bitmap surface
	m_bob.width  = width;
	m_bob.height = height;

	// set all images to null
	for (index=0; index<MAX_BOB_FRAMES; index++)
		m_bob.images[index] = NULL;

	// set all animations to null
	for (index=0; index<MAX_BOB_ANIMATIONS; index++)
		m_bob.animations[index] = NULL;

#if 0
	// make sure surface width is a multiple of 8, some old version of dd like that
	// now, it's unneeded...
	m_bob.width_fill = ((width%8!=0) ? (8-width%8) : 0);
	Write_Error("\nCreate BOB: width_fill=%d",m_bob.width_fill);
#endif

	// now create each surface
	for (index=0; index<m_bob.num_frames; index++)
	{
		// set to access caps, width, and height
		memset(&ddsd,0,sizeof(ddsd));
		ddsd.dwSize  = sizeof(ddsd);
		ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;

		ddsd.dwWidth  = m_bob.width + m_bob.width_fill;
		ddsd.dwHeight = m_bob.height;

		// set surface to offscreen plain
		ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | mem_flags;

		// create the surfaces, return failure if problem
		if (FAILED(m_lpdd->CreateSurface(&ddsd, &(m_bob.images[index]), NULL)))
			return -1;

		// set color key to default color 000
		// note that if this is a 8bit bob then palette index 0 will be 
		// transparent by default
		// note that if this is a 16bit bob then RGB value 000 will be 
		// transparent
		DDCOLORKEY color_key; // used to set color key
		color_key.dwColorSpaceLowValue  = color_key_value;
		color_key.dwColorSpaceHighValue = color_key_value;

		// now set the color key for source blitting
		(m_bob.images[index])->SetColorKey(DDCKEY_SRCBLT, &color_key);

	} // end for index

	// return success
	return 0;

} // end Create_BOB

int CGameBob::Load_Frame(BITMAP_FILE_PTR bitmap, // bitmap to scan image data from
						 int frame,       // frame to load
						 int cx,int cy,   // cell or absolute pos. to scan image from
						 int mode)        // if 0 then cx,cy is cell position, else 
						 // cx,cy are absolute coords
{
	// this function extracts a bitmap out of a bitmap file

	DDSURFACEDESC2 ddsd;  //  direct draw surface description 

	UCHAR *source_ptr,   // working pointers
		*dest_ptr;

	// test the mode of extraction, cell based or absolute
	if (mode==BITMAP_EXTRACT_MODE_CELL)
	{
		// re-compute x,y
		cx = cx*(m_bob.width+1) + 1;
		cy = cy*(m_bob.height+1) + 1;
	} // end if

	// extract bitmap data
	source_ptr = bitmap->buffer + cy*bitmap->bitmapinfoheader.biWidth+cx;

	// get the addr to destination surface memory

	// set size of the structure
	ddsd.dwSize = sizeof(ddsd);

	// lock the display surface
	(m_bob.images[frame])->Lock(NULL,
		&ddsd,
		DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR,
		NULL);

	// assign a pointer to the memory surface for manipulation
	dest_ptr = (UCHAR *)ddsd.lpSurface;

	// iterate thru each scanline and copy bitmap
	for (int index_y=0; index_y<m_bob.height; index_y++)
	{
		// copy next line of data to destination
		memcpy(dest_ptr, source_ptr,m_bob.width);

		// advance pointers
		dest_ptr   += (ddsd.lPitch); // (m_bob.width+m_bob.width_fill);
		source_ptr += bitmap->bitmapinfoheader.biWidth;
	} // end for index_y

	// unlock the surface 
	(m_bob.images[frame])->Unlock(NULL);

	// set state to loaded
	m_bob.attr |= BOB_ATTR_LOADED;

	// return success
	return 0;

} // end Load_Frame_BOB

int CGameBob::Load_Animation(int anim_index, 
							 int num_frames, 
							 int *sequence)
{
	// this function load an animation sequence for a bob
	// the sequence consists of frame indices, the function
	// will append a -1 to the end of the list so the display
	// software knows when to restart the animation sequence

	// allocate memory for bob animation
	if (!(m_bob.animations[anim_index] = (int *)malloc((num_frames+1)*sizeof(int))))
		return -1;

	// load data into 
	int index=0;
	for (index=0; index<num_frames; index++)
		m_bob.animations[anim_index][index] = sequence[index];

	// set the end of the list to a -1
	m_bob.animations[anim_index][index] = -1;

	// return success
	return 0;

} // end Load_Animation_BOB


int CGameBob::SetPos(int x, int y)
{
	// this functions sets the postion of a bob

	// set positin
	m_bob.x = x;
	m_bob.y = y;

	// return success
	return 0;
} // end Set_Pos

///////////////////////////////////////////////////////////

int CGameBob::SetAnimSpeed(int speed)
{
	// this function simply sets the animation speed of a bob
	// set speed
	m_bob.anim_count_max = speed;

	// return success
	return 0;

} // end Set_Anim_Speed

///////////////////////////////////////////////////////////

int CGameBob::SetAnimation(int anim_index)
{
	// this function sets the animation to play

	// set the animation index
	m_bob.curr_animation = anim_index;

	// reset animation 
	m_bob.anim_index = 0;

	// return success
	return 0;

} // end Set_Animation_BOB

///////////////////////////////////////////////////////////

int CGameBob::SetVel(int xv, int yv)
{
	// this function sets the velocity of a bob

	// set velocity
	m_bob.xv = xv;
	m_bob.yv = yv;

	// return success
	return 0;
} // end Set_Vel_BOB

///////////////////////////////////////////////////////////

int CGameBob::Hide()
{
	// this functions hides bob 

	// reset the visibility bit
	RESET_BIT(m_bob.attr, BOB_ATTR_VISIBLE);

	// return success
	return 0;
} // end Hide_BOB

///////////////////////////////////////////////////////////

int CGameBob::Show()
{
	// this function shows a bob

	// set the visibility bit
	SET_BIT(m_bob.attr, BOB_ATTR_VISIBLE);

	// return success
	return 0;
} // end Show_BOB

///////////////////////////////////////////////////////////

int CGameBob::CollisionBOBS(CGameBob* pGameBob)
{
	// are these a valid bobs
	if (pGameBob == NULL)
		return -1;

	BOB* pBob = pGameBob->GetBob();

	// get the radi of each rect
	int width1  = (m_bob.width>>1) - (m_bob.width>>3);
	int height1 = (m_bob.height>>1) - (m_bob.height>>3);

	int width2  = (pBob->width>>1) - (pBob->width>>3);
	int height2 = (pBob->height>>1) - (pBob->height>>3);

	// compute center of each rect
	int cx1 = m_bob.x + width1;
	int cy1 = m_bob.y + height1;

	int cx2 = pBob->x + width2;
	int cy2 = pBob->y + height2;

	// compute deltas
	int dx = abs(cx2 - cx1);
	int dy = abs(cy2 - cy1);

	// test if rects overlap
	if (dx < (width1+width2) && dy < (height1+height2))
		return 0;
	else
		// else no collision
		return 1;

} // end Collision_BOBS

int CGameBob::Move(int x, int y)
{
	// is the player moving?
	m_bob.x	+= x;
	m_bob.y	+= y;

	// test bounds
	if ( m_bob.x > (m_bob.maxX-32) )
		m_bob.x = m_bob.maxX-32;
	else
		if (m_bob.x < m_bob.minX)
			m_bob.x = m_bob.minX;

	if (m_bob.y > (m_bob.maxY-32))
		m_bob.y = m_bob.maxY-32;
	else
		if (m_bob.y < m_bob.minY)
			m_bob.y = m_bob.minY;

	return 0;
}

//////////////////////////////////////////////////////////

int CGameBob::Animate()
{
	// this function animates a bob, basically it takes a look at
	// the attributes of the bob and determines if the bob is 
	// a single frame, multiframe, or multi animation, updates
	// the counters and frames appropriately

	// test the level of animation
	if (m_bob.attr & BOB_ATTR_SINGLE_FRAME)
	{
		// current frame always = 0
		m_bob.curr_frame = 0;
		return 0;
	} // end if
	else
		if (m_bob.attr & BOB_ATTR_MULTI_FRAME)
		{
			// update the counter and test if its time to increment frame
			if (++m_bob.anim_counter >= m_bob.anim_count_max)
			{
				// reset counter
				m_bob.anim_counter = 0;

				// move to next frame
				if (++m_bob.curr_frame >= m_bob.num_frames)
					m_bob.curr_frame = 0;

			} // end if

		} // end elseif
		else
			if (m_bob.attr & BOB_ATTR_MULTI_ANIM)
			{
				// this is the most complex of the animations it must look up the
				// next frame in the animation sequence

				// first test if its time to animate
				if (++m_bob.anim_counter >= m_bob.anim_count_max)
				{
					// reset counter
					m_bob.anim_counter = 0;

					// increment the animation frame index
					m_bob.anim_index++;

					// extract the next frame from animation list 
					m_bob.curr_frame = m_bob.animations[m_bob.curr_animation][m_bob.anim_index];

					// is this and end sequence flag -1
					if (m_bob.curr_frame == -1)
					{
						// test if this is a single shot animation
						if (m_bob.attr & BOB_ATTR_ANIM_ONE_SHOT)
						{
							// set animation state message to done
							m_bob.anim_state = BOB_STATE_ANIM_DONE;

							// reset frame back one
							m_bob.anim_index--;

							// extract animation frame
							m_bob.curr_frame = m_bob.animations[m_bob.curr_animation][m_bob.anim_index];    

						} // end if
						else
						{
							// reset animation index
							m_bob.anim_index = 0;

							// extract first animation frame
							m_bob.curr_frame = m_bob.animations[m_bob.curr_animation][m_bob.anim_index];
						} // end else

					}  // end if

				} // end if

			} // end elseif

			// return success
			return 0;

} // end Amimate_BOB


int CGameBob::Draw(LPDIRECTDRAWSURFACE7 dest) // surface to draw the bob on
{
	// draw a bob at the x,y defined in the BOB
	// on the destination surface defined in dest

	RECT dest_rect,   // the destination rectangle
		source_rect; // the source rectangle                             

	// is bob visible
	if (!(m_bob.attr & BOB_ATTR_VISIBLE))
		return -1;

	// fill in the destination rect
	dest_rect.left   = m_bob.x;
	dest_rect.top    = m_bob.y;
	dest_rect.right  = m_bob.x + m_bob.width;
	dest_rect.bottom = m_bob.y + m_bob.height;

	// fill in the source rect
	source_rect.left    = 0;
	source_rect.top     = 0;
	source_rect.right   = m_bob.width;
	source_rect.bottom  = m_bob.height;

	// blt to destination surface
	if (FAILED(dest->Blt(&dest_rect, m_bob.images[m_bob.curr_frame],
		&source_rect,(DDBLT_WAIT | DDBLT_KEYSRC),
		NULL)))
		return -1;

	// return success
	return 0;
} // end Draw_BOB


int CGameBob::Destroy()
{
	// destroy the BOB, tests if this is a real bob or a clone
	// if real then release all the memory, otherwise, just resets
	// the pointers to null

	int index; // looping var

	// test if this is a clone
	if ((m_bob.attr & BOB_ATTR_CLONE))
	{
		// null link all surfaces
		for (index=0; index<MAX_BOB_FRAMES; index++)
			if (m_bob.images[index])
				m_bob.images[index]=NULL;

		// release memory for animation sequences 
		for (index=0; index<MAX_BOB_ANIMATIONS; index++)
			if (m_bob.animations[index])
				m_bob.animations[index]=NULL;

	} // end if
	else
	{
		// destroy each bitmap surface
		for (index=0; index<MAX_BOB_FRAMES; index++)
			if (m_bob.images[index])
			{
				(m_bob.images[index])->Release();
				m_bob.images[index]=NULL;
			}

		// release memory for animation sequences 
		for (index=0; index<MAX_BOB_ANIMATIONS; index++)
			if (m_bob.animations[index])
			{
				free(m_bob.animations[index]);
				m_bob.animations[index]=NULL;
			}

	} // end else not clone

	// return success
	return 0;

} // end Destroy_BOB